using WMM.Tapioca;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Configuration;
using System.IO;
using System.Text;
using System.Xml;
using System.Net;
using WMM.Tapioca.Instance;
using WMM.Tapioca.Taxonomy;
using WMM.Tapioca.Linkbase;
using WMM.Tapioca.Validate;
using WMM.Tapioca.Utilities;

namespace WMM.Test.Tapioca
{
	/// <summary>
	/// This class executes the XBRL-CONF-CR3-2007-03-05 conformance test suite.
	/// </summary>
	/// <remarks>
	/// The test suite is driven by a document in the root of the conformance suite
	/// folder named "xbrl.xml". The root document is an XML document that contains
	/// a set of &lt;testcase&gt; elements, each of which references a separate
	/// test case document.
	/// </remarks>
	[TestClass]
	public class XBRLCONFCR520120124Test
	{
        private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

        static XmlCachingResolver resolver;

        [ClassInitialize()]
        public static void MyClassInitialize(TestContext testContext)
        {
            bool useProxy = Boolean.Parse(ConfigurationManager.AppSettings["UseProxy"]);
            resolver = new XmlCachingResolver(true, useProxy);    
        }

		private int thisTestsPassed;

		public XBRLCONFCR520120124Test()
		{
		}

		#region Additional test attributes
		//
		// You can use the following additional attributes as you write your tests:
		//
		// Use ClassInitialize to run code before running the first test in the class
		// [ClassInitialize()]
		// public static void MyClassInitialize(TestContext testContext) { }
		//
		// Use ClassCleanup to run code after all tests in a class have run
		// [ClassCleanup()]
		// public static void MyClassCleanup() { }
		//
		// Use TestInitialize to run code before running each test 
		// [TestInitialize()]
		// public void MyTestInitialize() { }
		//
		// Use TestCleanup to run code after each test has run
		// [TestCleanup()]
		// public void MyTestCleanup() { }
		//
		#endregion

		[TestMethod]
        [Description("XBRL-CONF-CR5-2012-01-24")]
		public void XBRLCONFCR520120124Testcases()
		{
			thisTestsPassed = 0;
			var conformanceXmlSource = ConfigurationManager.AppSettings["ConformanceXbrl"];
			var conformanceXmlSourcePath = Path.GetDirectoryName(conformanceXmlSource);
			var conformanceXmlDocument = new XmlDocument();
			conformanceXmlDocument.Load(conformanceXmlSource);
			var TestcaseNodes = conformanceXmlDocument.SelectNodes("//testcase");

			foreach (XmlNode TestcaseNode in TestcaseNodes)
			{
				ExecuteTestcase(conformanceXmlSourcePath, TestcaseNode);
			}
		}

		/// <summary>
		/// Executes the test case referenced in the supplied test case node.
		/// </summary>
		/// <param name="ConformanceXmlSourcePath">
		/// The path to the conformance suite.
		/// </param>
		/// <param name="TestcaseNode">
		/// A reference to one of the &lt;testcase&gt; elements in the root test suiteb ocument.
		/// </param>
		private void ExecuteTestcase(string ConformanceXmlSourcePath, XmlNode TestcaseNode)
		{
			var uriAttribute = TestcaseNode.Attributes["uri"];
			var testcaseXmlSource = uriAttribute.Value;
			var testcaseXmlSourceFullPathBuilder = new StringBuilder();
			testcaseXmlSourceFullPathBuilder.AppendFormat("{0}{1}{2}", ConformanceXmlSourcePath, Path.DirectorySeparatorChar, testcaseXmlSource);
			var testcaseXmlSourceFullPath = testcaseXmlSourceFullPathBuilder.ToString();
			var testcaseXmlSourceDirectory = Path.GetDirectoryName(testcaseXmlSourceFullPath);
			var testcaseXmlDocument = new XmlDocument();
			testcaseXmlDocument.Load(testcaseXmlSourceFullPath);
			var variationNodes = testcaseXmlDocument.SelectNodes("//variation");

			foreach (XmlNode VariationNode in variationNodes)
			{
				ExecuteVariation(testcaseXmlSourceDirectory, VariationNode);
			}
		}

		
		private void ExecuteVariation(string TestcaseXmlSourceDirectory, XmlNode VariationNode)
		{
            XbrlException VariationException = null;
            bool isValidated = true;

			var currentVariation = new TestVariation(VariationNode);

            if (currentVariation.isReadInstanceFirst)
            {
                if (string.IsNullOrEmpty(currentVariation.Instance) == true)
                {
                    return;
                }

                var instanceXmlSourceFullPathBuilder = new StringBuilder();
                instanceXmlSourceFullPathBuilder.AppendFormat("{0}{1}{2}", TestcaseXmlSourceDirectory, Path.DirectorySeparatorChar, currentVariation.Instance);
                var instanceXmlSourceFullPath = instanceXmlSourceFullPathBuilder.ToString();

                try
                {
                    var newInstanceDocument = new InstanceDocument(instanceXmlSourceFullPath, resolver);

                    XbrlManager xbrlManager = new XbrlManager(newInstanceDocument);                    
                    xbrlManager.Validate();

                    if (TestcaseXmlSourceDirectory.Contains("300-instance"))
                    {
                        isValidated = newInstanceDocument.Dts.ValidatedInstance && newInstanceDocument.Dts.ValidatedSchema;
                    }

                    if (TestcaseXmlSourceDirectory.Contains("200-linkbase"))
                    {
                        isValidated = newInstanceDocument.Dts.ValidatedInstance && newInstanceDocument.Dts.ValidatedLinkbases;
                    }

                    if (TestcaseXmlSourceDirectory.Contains("100-schema"))
                    {
                        isValidated = newInstanceDocument.Dts.ValidatedInstance && newInstanceDocument.Dts.ValidatedSchema;
                    }

                    if (TestcaseXmlSourceDirectory.Contains("400-misc"))
                    {
                        isValidated = newInstanceDocument.Dts.ValidatedInstance && newInstanceDocument.Dts.ValidatedLinkbases;
                    }
                }
                catch (Exception ex)
                {
                    log.Error("Error:", ex);
                    isValidated = false;
                }
            }

            if (currentVariation.isReadXsdFirst || currentVariation.isReadLinkbaseFirst)
            {
                try
                {
                    DTS dts = null;
                    TaxonomySchema ts = null;

                    if (currentVariation.isReadXsdFirst)
                    {
                        if (!string.IsNullOrEmpty(currentVariation.Xsd))
                        {

                            var schemaXmlSourceFullPathBuilder = new StringBuilder();
                            schemaXmlSourceFullPathBuilder.AppendFormat("{0}{1}{2}", TestcaseXmlSourceDirectory, Path.DirectorySeparatorChar, currentVariation.Xsd);

                            string XsdSourceFullPath = schemaXmlSourceFullPathBuilder.ToString();

                            log.Info("\n\nCreating schema: " + XsdSourceFullPath);
                            ts = new TaxonomySchema(dts, XsdSourceFullPath, null, "", resolver);
                            dts = new DTS(ts, resolver);
                        }

                    }

                    if (currentVariation.isReadLinkbaseFirst)
                    {
                        if (string.IsNullOrEmpty(currentVariation.Linkbase))
                        {
                            return;
                        }

                        var linkbaseXmlSourceFullPathBuilder = new StringBuilder();
                        linkbaseXmlSourceFullPathBuilder.AppendFormat("{0}{1}{2}", TestcaseXmlSourceDirectory, Path.DirectorySeparatorChar, currentVariation.Linkbase);
                        string LinkbaseSourceFullPath = linkbaseXmlSourceFullPathBuilder.ToString();
                        LinkbaseDocument linkbase = new LinkbaseDocument(dts, null, "", LinkbaseSourceFullPath, "", resolver);
                        dts = new DTS(linkbase, resolver);

                        if (!ContainsLinkbase(dts, linkbase.FileName))
                        {
                            log.Info("Creating LinkbaseDocument: " + linkbase.FileName);
                            dts.LinkbaseDocuments.Add(linkbase);
                        }
                    };

                    XbrlManager xbrlManager = new XbrlManager(null);

                    if (TestcaseXmlSourceDirectory.Contains("200-linkbase") ||
                        TestcaseXmlSourceDirectory.Contains("300-instance"))
                    {
                        ValidateLinkbases vlb = new ValidateLinkbases(dts);
                        isValidated = dts.ValidatedLinkbases;
                    }

                    if (TestcaseXmlSourceDirectory.Contains("100-schema"))
                    {
                        ValidateDts vDts = new ValidateDts(dts);
                        isValidated = dts.ValidatedSchema;
                    }
                    if (TestcaseXmlSourceDirectory.Contains("400-misc"))
                    {
                        isValidated = dts.ValidatedInstance;
                    }

                    if (TestcaseXmlSourceDirectory.Contains("related-standards"))
                    {
                        ValidateLinkbases vlb = new ValidateLinkbases(dts);
                        isValidated = dts.ValidatedSchema && dts.ValidatedLinkbases;
                    }
                }
                catch (Exception ex)
                {
                    log.Error("Error:", ex);
                    isValidated = false;
                }
            }

            if ((isValidated == true) && (currentVariation.isValidityExpected == false))
            {
                AnnounceTestFailure(currentVariation);
            }
            if ((isValidated == false) && (currentVariation.isValidityExpected == true))
            {
                AnnounceTestFailure(currentVariation, VariationException);
            }

			thisTestsPassed++;
		}

		
		private void AnnounceTestFailure(TestVariation CurrentVariation, Exception VariationException)
		{
			var failMessage = new StringBuilder();

			failMessage.AppendFormat("Instance: {0}{1}", CurrentVariation.Instance, Environment.NewLine);
			failMessage.AppendFormat("Name: {0}{1}", CurrentVariation.Name, Environment.NewLine);
			failMessage.AppendFormat("Description: {0}", CurrentVariation.Description);

			if (VariationException != null)
			{
				failMessage.AppendFormat("{0}Exception Type: {1}{2}", Environment.NewLine, VariationException.GetType().ToString(), Environment.NewLine);
				failMessage.AppendFormat("Exception Description: {0}", VariationException.Message);
			}
			Assert.Fail(failMessage.ToString());
		}

		
		private void AnnounceTestFailure(TestVariation CurrentVariation)
		{
			AnnounceTestFailure(CurrentVariation, null);
		}


        private bool ContainsLinkbase(DTS dts, string nameLinkbase)
        {
            foreach (LinkbaseDocument linkbaseDocument in dts.LinkbaseDocuments)
            {
                if (linkbaseDocument.FileName.Equals(nameLinkbase))
                {
                    return true;
                }
            }

            return false;
        }

	}
}
